#include <grpcpp/grpcpp.h>
#include "message.grpc.pb.h"
#include "const.h"
#include "Singleton.h"
#include "ConfigMgr.h"
using grpc::Channel;
using grpc::Status;
using grpc::ClientContext;
using message::GetVarifyReq;
using message::GetVarifyRsp;
using message::VarifyService;

class  RPCPool {
public:
    RPCPool(std::size_t poolSize,std::string host,std::string port):_poolSize(poolSize),_host(host),
    _port(port),_b_stop(false){
        for (int i = 0; i < poolSize; ++i) {
            std::shared_ptr<Channel>channel = grpc::CreateChannel(host + ":" + port, grpc::InsecureChannelCredentials());
            _connections.push(VarifyService::NewStub(channel));
        }
    }
    ~RPCPool() {
        std::lock_guard<std::mutex> lock(_mutex);
        Close();
        while(!_connections.empty()) {
            _connections.pop();
        }
    }
    std::unique_ptr<VarifyService::Stub>getConnection() {
        std::unique_lock<std::mutex> lock(_mutex);
        _cond.wait(lock, [this]() {
            if (_b_stop) {
                return true;
            }
            return !_connections.empty();
            });
        if (_b_stop) {
            return nullptr;
        }
        auto context = std::move(_connections.front());
        _connections.pop();
        return context;
    }
    void returnConnection(std::unique_ptr<VarifyService::Stub> context) {
        std::lock_guard<std::mutex>lock(_mutex);
        if (_b_stop) {
            return;
        }
        _connections.push(std::move(context));
        _cond.notify_one();
    }
    void Close() {
        _b_stop = true;
        _cond.notify_all();
    }
private:
    std::size_t _poolSize;
    std::atomic<bool> _b_stop;
    std::string _host;
    std::string _port;
    std::queue<std::unique_ptr<VarifyService::Stub>>_connections;
    std::condition_variable _cond;
    std::mutex _mutex;

};
class VarifyGrpcClient :public Singleton<VarifyGrpcClient>
{
    friend class Singleton<VarifyGrpcClient>;
public:
    GetVarifyRsp GetVarifyCode(std::string email) {
        ClientContext context;
        GetVarifyRsp reply;
        GetVarifyReq request;
        request.set_email(email);
        auto stub_ = _pool->getConnection();
        Status status = stub_->GetVarifyCode(&context, request, &reply);
        if (status.ok()) {
            _pool->returnConnection(std::move(stub_));
            return reply;
        }
        else {
            _pool->returnConnection(std::move(stub_));
            reply.set_error(ErrorCodes::RPCFailed);
            return reply;
        }
    }

    std::unique_ptr<RPCPool>_pool;
private:
    VarifyGrpcClient() {
        auto& gCfgMgr = ConfigMgr::Inst();
        std::string host = gCfgMgr["VarifyServer"]["Host"];
        std::string port = gCfgMgr["VarifyServer"]["Port"];
        _pool.reset(new RPCPool(5, host, port));

        
    }
};